package org.zjx.window;

import com.alibaba.fastjson2.JSON;
import com.sun.jna.platform.win32.WinDef;
import lombok.Data;
import lombok.SneakyThrows;
import org.zjx.common.entity.Rect;
import org.zjx.operate.GameMouse;
import org.zjx.operate.GameOperate;
import org.zjx.operate.IOperate;
import org.zjx.operate.RobotOperate;
import org.zjx.resolution.resolution.GameWindowResolution;
import org.zjx.resolution.resolution.LoginWindowResolution;
import org.zjx.resource.ResourceManager;
import org.zjx.util.HwndUtil;
import org.zjx.util.ImageUtil;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.List;

@Data
public class GameWindow implements IOperate {
    public static final String WINDOW_STATUS_START = "start";
    public static final String WINDOW_STATUS_LOGIN = "login";
    public static final String WINDOW_STATUS_GAME = "game";

    private String windowStatus = WINDOW_STATUS_LOGIN;
    private Point initPoint;

    private WinDef.HWND hwnd = null;
    private GameOperate gameOperate;

    private BufferedImage screenshot = null;

    private Point mouseNow = null; //后台鼠标坐标
    private GameMouse gameMouse = null; //界面上的鼠标坐标以及样式

    public GameWindow(Point initPoint) {
        this.initPoint = initPoint;
        this.gameOperate = new GameOperate(this);
    }

    public void init(WinDef.HWND hwnd, String windowStatus) {
        this.hwnd = hwnd;
        if (windowStatus != null) this.windowStatus = windowStatus;
        if (initPoint != null) move(initPoint);
    }

    //窗口界面大小
    public Rect getMockBounds() {
        Rect rect = getTrueBounds();
        return new Rect(0,0,rect.getRight() - rect.getLeft() + 1,rect.getBottom() - rect.getTop() + 1);
    }


    public Rect getTrueBounds() {
        Rect rect = HwndUtil.getWindowRect(hwnd);
        Rect boundary = getBoundary();
        return new Rect(rect.getLeft()+boundary.getLeft(),rect.getTop()+boundary.getTop(),rect.getRight()+boundary.getRight(),rect.getBottom()+boundary.getBottom());
    }

    private Rect getBoundary() {
        Rect boundary = new Rect(0, 0, 0, 0);
        switch (windowStatus) {
            case WINDOW_STATUS_START:
                break;
            case WINDOW_STATUS_LOGIN:
                LoginWindowResolution loginWindowResolution = new LoginWindowResolution();
                loginWindowResolution.loadResolution();
                boundary = JSON.parseObject(loginWindowResolution.getProperty(LoginWindowResolution.LOGIN_WINDOW_TRUE_RECT).toString()).toJavaObject(Rect.class) ;
                break;
            case WINDOW_STATUS_GAME:
                GameWindowResolution gameWindowResolution = new GameWindowResolution();
                gameWindowResolution.loadResolution();
                boundary = JSON.parseObject(gameWindowResolution.getProperty(GameWindowResolution.GAME_WINDOW_TRUE_RECT).toString()).toJavaObject(Rect.class) ;
                break;
            default:
                throw new RuntimeException("游戏界面类型不存在！！！");
        }
        return boundary;
    }


    public BufferedImage getScreenshot() {
        if (screenshot == null) flush();
        return screenshot;
    }

    public void preposition() {
        HwndUtil.preposition(hwnd);
    }

    public void move(Point point) {
        HwndUtil.moveWindow(hwnd, point.x, point.y);
    }

    @SneakyThrows
    public void flush() {
        preposition();
        delay(10);
        screenshot = RobotOperate.getInstance().getImage(getTrueBounds());
    }

    public void flushGameMouse() {
        if (screenshot == null) flush();
        gameMouse = findMouse();
    }


    //region 查找游戏鼠标坐标

    /**
     * 查找游戏鼠标坐标
     */
    public GameMouse findMouse() {
        if (screenshot == null) throw new RuntimeException("游戏截屏数据为空！！！");
        GameMouse gameMouse = findOrdinaryMouse();
        if (gameMouse != null) return gameMouse;
        gameMouse = findNpcMouse();
        if (gameMouse != null) return gameMouse;
        gameMouse = findMonsterMouse();
        if (gameMouse != null) return gameMouse;
        gameMouse = findWriteMouse();
        if (gameMouse != null) return gameMouse;
        gameMouse = findFingerMouse();
        return gameMouse;
    }

    private GameMouse findOrdinaryMouse() {
        BufferedImage image = ImageUtil.stringToBufferedImage(GameMouse.MOUSE_IMAGE);
        List<Point> points = ImageUtil.matchTemplate(screenshot, image);
        if (!points.isEmpty()) {
            Point point = points.size() >= 2 ? points.get(1) : points.get(0);
            return new GameMouse(new Rect(point.x, point.y, point.x + image.getWidth() + 1, point.y + image.getHeight() + 1), GameMouse.Type.ORDINARY);
        }
        return null;
    }

    private GameMouse findNpcMouse() {
        BufferedImage image = ImageUtil.stringToBufferedImage(GameMouse.MOUSE_NPC_IMAGE);
        List<Point> points = ImageUtil.matchTemplate(screenshot, image);
        if (!points.isEmpty()) {
            Point point = points.size() >= 2 ? points.get(1) : points.get(0);
            return new GameMouse(new Rect(point.x, point.y, point.x + image.getWidth() + 1, point.y + image.getHeight() + 1), GameMouse.Type.NPC);
        }
        return null;
    }

    private GameMouse findMonsterMouse() {
        BufferedImage image = ImageUtil.stringToBufferedImage(GameMouse.MOUSE_MONSTER_IMAGE);
        List<Point> points = ImageUtil.matchTemplate(screenshot, image);
        if (!points.isEmpty()) {
            Point point = points.size() >= 2 ? points.get(1) : points.get(0);
            return new GameMouse(new Rect(point.x, point.y, point.x + image.getWidth() + 1, point.y + image.getHeight() + 1), GameMouse.Type.MONSTER);
        }
        return null;
    }

    private GameMouse findFingerMouse() {
        BufferedImage image = ImageUtil.stringToBufferedImage(GameMouse.MOUSE_FINGER_IMAGE);
        List<Point> points = ImageUtil.matchTemplate(screenshot, image);
        if (!points.isEmpty()) {
            Point point = points.size() >= 2 ? points.get(1) : points.get(0);
            return new GameMouse(new Rect(point.x, point.y, point.x + image.getWidth() + 1, point.y + image.getHeight() + 1), GameMouse.Type.FINGER);
        }
        return null;
    }

    private GameMouse findWriteMouse() {
        BufferedImage image = ImageUtil.stringToBufferedImage(GameMouse.MOUSE_WRITE);
        List<Point> points = ImageUtil.matchTemplate(screenshot, image);
        if (!points.isEmpty()) {
            Point point = points.size() >= 2 ? points.get(1) : points.get(0);
            return new GameMouse(new Rect(point.x, point.y, point.x + image.getWidth() + 1, point.y + image.getHeight() + 1), GameMouse.Type.WRITE);
        }
        return null;

        //endregion

    }

    @Override
    public BufferedImage getImage(Rect rect) {
        return ImageUtil.cutImage(screenshot, rect);
    }

    @Override
    public Color getPixelColor(int x, int y) {
        return new Color(screenshot.getRGB(x, y));
    }

    @Override
    public void moveMouse(int x, int y) {
        if (windowStatus.equals(WINDOW_STATUS_START)) {
            Rect rect = getTrueBounds();
            RobotOperate.getInstance().moveMouse(rect.getLeft() + x, rect.getTop() + y);
            return;
        }

        if (windowStatus.equals(WINDOW_STATUS_LOGIN)) {
            gameOperate.moveMouse(x, y);
            return;
        }

        if (isRightBorder(x, y)) {
            moveRightBorder(x, y);
            return;
        }

        if (mouseNow == null)
            gameOperate.moveMouse(x, y);

        int nullCont = 0;
        while (true) {
            flush();
            flushGameMouse();
            if (gameMouse == null) {
                nullCont++;
                if (nullCont > 5) {
                    gameOperate.moveMouse(x, y);
                    if (nullCont > 10) {
                        Point size = ResourceManager.CONFIG.getWindowSize();
                        gameOperate.moveMouse(size.x/2, size.y/2);
                        nullCont = 0;
                    }
                }
                delay(10);
                continue;
            }

            int X = x - gameMouse.rect.getLeft();
            int Y = y - gameMouse.rect.getTop();

            // 如果偏差小于3像素，认为已接近目标
            if (Math.abs(X) < 2 && Math.abs(Y) < 2) return;

            // 对半分步移动：将当前偏差分成一半，先移动一半
            int halfX = X / 2;
            int halfY = Y / 2;

            // 计算新的目标位置（偏差的中间点）
            // 移动到新的目标位置
            gameOperate.moveMouse(mouseNow.x + halfX, mouseNow.y + halfY);

            // 等待一段时间，避免太快导致抖动
            delay(30);
        }
    }

    public void moveRightBorder(int x, int y){
        Point size = ResourceManager.CONFIG.getWindowSize();
        BufferedImage mouse = ImageUtil.stringToBufferedImage(GameMouse.MOUSE_IMAGE);
        moveMouse(size.x - mouse.getWidth(),y);
        delay(10);
        int intervalX = x - size.x + mouse.getWidth();
        gameOperate.moveMouse(mouseNow.x + intervalX,mouseNow.y);
    }

    public boolean isRightBorder(int x, int y){
        Point size = ResourceManager.CONFIG.getWindowSize();
        BufferedImage mouse = ImageUtil.stringToBufferedImage(GameMouse.MOUSE_IMAGE);
        return x > size.x - mouse.getWidth();
    }

    @Override
    public void clickMouse(Point point) {
        moveMouse(point.x, point.y);
        delay(30);
        clickMouse(1);
    }

    @Override
    public void clickMouse(int button) {
        if (windowStatus.equals(WINDOW_STATUS_START)) {
            RobotOperate.getInstance().clickMouse(button);
            return;
        }
        gameOperate.clickMouse(button);
    }

    @Override
    public void clickMouse(int button, MouseAction action) {
        if (windowStatus.equals(WINDOW_STATUS_START)) {
            RobotOperate.getInstance().clickMouse(button, action);
            return;
        }
        gameOperate.clickMouse(button, action);
    }

    @Override
    public void scrollMouse(int amount) {
        if (windowStatus.equals(WINDOW_STATUS_START)) {
            RobotOperate.getInstance().scrollMouse(amount);
            return;
        }
        gameOperate.scrollMouse(amount);
    }

    @Override
    public void dragMouse(int startX, int startY, int endX, int endY) {
        if (Math.abs(endY - startY) < 2 && Math.abs(endX - startX) < 2) {
            // 生成5-10之间的随机数
            int randomOffset = 5 + (int)(Math.random() * 6); // 5-10

            // 随机决定正负方向
            int signX = Math.random() > 0.5 ? 1 : -1;
            int signY = Math.random() > 0.5 ? 1 : -1;

            // 应用随机位移
            endX = startX + signX * randomOffset;
            endY = startY + signY * randomOffset;
        }
        moveMouse(startX, startY); // 移动到起始位置
        // 按下左键开始拖动
        clickMouse(1, MouseAction.DOWN);
        // 持续移动鼠标到目标位置
        moveMouse(endX, endY);
        // 松开左键完成拖动
        clickMouse(1, MouseAction.UP);
    }

    @Override
    public void write(String str) {
        if (windowStatus.equals(WINDOW_STATUS_START)) {
            RobotOperate.getInstance().write(str);
            return;
        }
        gameOperate.write(str);
    }

    @Override
    public void pressKeyCombination(int... keyCodes) {
        if (windowStatus.equals(WINDOW_STATUS_START)) {
            RobotOperate.getInstance().pressKeyCombination(keyCodes);
            return;
        }
        gameOperate.pressKeyCombination(keyCodes);
    }

    @Override
    public void delay(int millis) {
        if (windowStatus.equals(WINDOW_STATUS_START)) {
            RobotOperate.getInstance().delay(millis);
            return;
        }
        gameOperate.delay(millis);
    }


    @Override
    public String toString() {
        return "GameWindow{" +
                "windowStatus='" + windowStatus + '\'' +
                ", initPoint=" + (initPoint != null ? initPoint.toString() : "null") +
                ", hwnd=" + (hwnd != null ? hwnd.toString() : "null") +
                '}';
    }
}
